package top.syfy.jwt.jwtdemo.util;

import com.auth0.jwt.interfaces.Claim;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import top.syfy.jwt.jwtdemo.annotation.FieldMarker;

/**
 * 实例对象与java.util.Map数据的相转工具类
 *
 * @author syfy
 * @date 2021/03/07
 */
public class Object2MapUtil {

	/**
	 * 将指定的类实例转为java.util.Map集合
	 *
	 * @param sourceBean 指定的类实例
	 * @param <T>        指定的类实例的类类型
	 * @return 指定的类实例转为java.util.Map集合
	 * @throws IllegalAccessException 非法访问异常
	 */
	public static <T> Map<String, Object> bean2Map(T sourceBean) throws IllegalAccessException {
		Map<String, Object> result = new HashMap<>();
		Class<?> sourceClass = sourceBean.getClass();
		// 获取指定类的所有字段(不包括从父类继承的字段)
		Field[] sourceFields = sourceClass.getDeclaredFields();
		for (Field field : sourceFields) {
			// 设置可访问,否则获取不到private修饰的字段
			field.setAccessible(true);
			// 获取字段上的注解,是否被@FieldMarker标记(JWT的负载数据)
			FieldMarker fm = field.getAnnotation(FieldMarker.class);
			if (Objects.nonNull(fm)) {
				result.put(field.getName(), field.get(sourceBean));
			}
			//field.setAccessible(false);
		}
		return result;
	}

	/**
	 * 将java.util.Map集合转为指定的类实例
	 *
	 * @param sourceMap java.util.Map集合
	 * @param instance  指定的类实例
	 * @param <T>       指定的类实例的类类型
	 * @return java.util.Map集合转为指定的类实例
	 * @throws NoSuchMethodException     没有这样的方法异常
	 * @throws IllegalAccessException    非法访问异常
	 * @throws InvocationTargetException 调用目标异常
	 * @throws InstantiationException    实例化异常
	 */
	public static <T> T map2Bean(Map<String, Object> sourceMap, Class<T> instance) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
		T obj = instance.getDeclaredConstructor().newInstance();
		Field[] sourceFields = obj.getClass().getDeclaredFields();
		for (Field field : sourceFields) {
			field.setAccessible(true);
			FieldMarker fm = field.getAnnotation(FieldMarker.class);
			if (Objects.nonNull(fm)) {
				Claim value = (Claim) sourceMap.get(field.getName());
				field.set(obj, value.as(field.getType()));
			}
		}
		return obj;
	}

}
